package com.lagou.filter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 定义全局过滤器，会对所有路由生效
 */
@Component
public class IPCountListFilter implements GlobalFilter, Ordered , InitializingBean {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Value("${spring.gateway.iplimit}")
    int limit;

    private volatile ConcurrentHashMap<String, List<Long>> callCache = new ConcurrentHashMap<>(128);

    /**
     * 过滤器核心方法
     * @param exchange 封装了request和response对象的上下文
     * @param chain 网关过滤器链（包含全局过滤器和单路由过滤器）
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        // 从request对象中获取客户端ip
        String clientIp = request.getRemoteAddress().getHostString();
        String s = request.getPath().toString();
        if (!s.contains("register")){
            return chain.filter(exchange);
        }

        if (!callCache.containsKey(clientIp)){
            callCache.put(clientIp, new ArrayList<Long>());
        }

        long curr = System.currentTimeMillis();
        callCache.get(clientIp).add(curr);
        //验证请求频次
        if (callCache.get(clientIp).size() > limit){
            // 决绝访问，返回
            response.setStatusCode(HttpStatus.FORBIDDEN); // 状态码
            logger.debug("=====>IP:" + clientIp + " 访问频繁，将被拒绝访问！");
            String data = "Request too busy!";
            DataBuffer wrap = response.bufferFactory().wrap(data.getBytes());
            return response.writeWith(Mono.just(wrap));
        }

        return chain.filter(exchange);
    }


    /**
     * 返回值表示当前过滤器的顺序(优先级)，数值越小，优先级越高
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }

    private ScheduledExecutorService scanDbexec = Executors.newScheduledThreadPool(1, Executors.defaultThreadFactory());
    @Override
    public void afterPropertiesSet() throws Exception {
        scanDbexec.scheduleWithFixedDelay(this::clearIpCache, 10, 5, TimeUnit.SECONDS);
    }

    private void clearIpCache() {
        long line = System.currentTimeMillis() - 5 * 60000;
        Set<Map.Entry<String, List<Long>>> entries = callCache.entrySet();
        for(Map.Entry<String, List<Long>> entry : entries){
            List<Long> list = entry.getValue();
            for (int i = 0; i < list.size(); i++) {
                if (list.get(i) > line){
                    break;
                }
                list.remove(i);
                i--;
            }
            if (list.size()==0){
                callCache.remove(entry.getKey());
            }
        }
    }

}
